﻿using System;
using System.Security;
using System.Web.Security;

namespace Actya.Core.Security
{
	public class AspnetMembershipService : IMembershipService
	{
		public string MembershipProviderName { get; private set; }
		public string RoleProviderName { get; private set; }

		public AspnetMembershipService(string membershipProviderName, string roleProviderName)
		{
			MembershipProviderName = membershipProviderName;
			RoleProviderName = roleProviderName;
		}

		protected MembershipProvider MembershipProvider
		{
			get
			{
				if (String.IsNullOrEmpty(MembershipProviderName))
				{
					throw new NullReferenceException("The MembershipProvider name is not set.");
				}
				return Membership.Providers[MembershipProviderName];
			}
		}

		protected RoleProvider RoleProvider
		{
			get
			{
				if (String.IsNullOrEmpty(RoleProviderName))
				{
					throw new NullReferenceException("The RoleProvider name is not set.");
				}
				return Roles.Providers[RoleProviderName];
			}
		}

		public User AuthenticateUser(string username, string password)
		{
			var isAuthenticated = MembershipProvider.ValidateUser(username, password);
			if (isAuthenticated)
			{
				var membershipUser = MembershipProvider.GetUser(username, false);
				var user = CreateUserFromMembershipUser(membershipUser);
				user.IsAuthenticated = true;
				user.Roles = RoleProvider.GetRolesForUser(user.Username);
				return user;
			}
			else
			{
				return new User { IsAuthenticated = false };
			}
		}

		public User GetUserByUsername(string username, bool isUserOnline)
		{
			var membershipUser = MembershipProvider.GetUser(username, true);
			if (membershipUser != null)
			{
				var user = CreateUserFromMembershipUser(membershipUser);
				user.Roles = RoleProvider.GetRolesForUser(username);
				return user;
			}
			return null;
		}

		public void CreateRole(string roleName)
		{
			// Just don't create the role when it doesn't exist.
			if (!RoleProvider.RoleExists(roleName))
			{
				RoleProvider.CreateRole(roleName);
			}
		}

		public User CreateUser(string username, string password, string defaultRole)
		{
			// Check existence first
			var existingUser = this.GetUserByUsername(username, false);
			if (existingUser != null) // existing user
			{
				existingUser = AuthenticateUser(username, password);
				if (! existingUser.IsAuthenticated)
				{
					throw new SecurityException(string.Format("The user {0} already exists, but the wrong password was suplied.", username));

				}
				// Check role membership
				if (!RoleProvider.IsUserInRole(username, defaultRole))
				{
					RoleProvider.AddUsersToRoles(new[] { username }, new[] { defaultRole });
				}
				return existingUser;
			}
			MembershipCreateStatus membershipCreateStatus;
			var membershipUser = MembershipProvider.CreateUser(username, password, null, null, null, true, null, out membershipCreateStatus);
			if (membershipCreateStatus != MembershipCreateStatus.Success)
			{
				throw new Exception(membershipCreateStatus.ToString());
			}
			RoleProvider.AddUsersToRoles(new []{username}, new []{defaultRole});
			return CreateUserFromMembershipUser(membershipUser);
		}

		private User CreateUserFromMembershipUser(MembershipUser membershipUser)
		{
			var user = new User();
			user.Username = membershipUser.UserName;
			user.Email = membershipUser.Email;
			user.DateCreated = membershipUser.CreationDate;
			user.DateLastLogin = membershipUser.LastLoginDate;
			return user;
		}
	}
}
